package com.wzp.util.idgenerate;

import java.util.Date;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicStampedReference;

class SnowFlakeIdGen {
    private long workerId;
    private long datacenterId;
    private long twepoch = 1629507739293L; //2021-08-21 09:02:19
    private long workerIdBits = 5L; //节点ID长度
    private long datacenterIdBits = 5L; //数据中心ID长度
    private long maxWorkerId = -1L ^ (-1L << workerIdBits); //最大支持机器节点数0~31，一共32个
    private long maxDatacenterId = -1L ^ (-1L << datacenterIdBits); //最大支持数据中心节点数0~31，一共32个
    private long sequenceBits = 12L; //序列号12位
    private long workerIdShift = sequenceBits; //机器节点左移12位
    private long datacenterIdShift = sequenceBits + workerIdBits; //数据中心节点左移17位
    private long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits; //时间毫秒数左移22位
    // 最大序列，12位
    private static final int MAX_SEQUENCE = (1 << 12) - 1;
    //最大时间戳， 41位
    private static final long MAX_TIME = (1L << 41) - 1;

    // 带时间的序列，原子操作
    private AtomicStampedReference<StampedSequence> current;

    public static final String salt = UUID.randomUUID().toString().replaceAll("-", "");
    public static final String[] saltArrays = salt.split("");
    private static final int ID_SIZE = 32;

    private static class IdGenHolder {
        private static final SnowFlakeIdGen instance = new SnowFlakeIdGen();
    }

    public static SnowFlakeIdGen get() {
        return IdGenHolder.instance;
    }

    public SnowFlakeIdGen() {
        Random random = new Random();
        int workerIdI = random.nextInt(31);
        long wokerId = Long.parseLong(String.valueOf(workerIdI));
        int datacenterIdI = random.nextInt(31);
        long datacenterId = Long.parseLong(String.valueOf(datacenterIdI));
        if (workerId > maxWorkerId || workerId < 0) {
            throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
        }
        if (datacenterId > maxDatacenterId || datacenterId < 0) {
            throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
        }
        StampedSequence dt = new StampedSequence(twepoch, 0);
        current = new AtomicStampedReference<StampedSequence>(dt, 0);
        this.workerId = workerId;
        this.datacenterId = datacenterId;
    }

    public String nextId() {
        StampedSequence seq = nextSequence();
        long time = seq.timestamp - twepoch;
        if (time > MAX_TIME) {
            throw new RuntimeException("Timestamp overflow! " + new Date(seq.timestamp));
        }
        long value = (time << timestampLeftShift)
                | (datacenterId << datacenterIdShift)
                | (workerId << workerIdShift) | seq.sequence;
        return slatNextId(value);
    }

    private String slatNextId(long originId) {
        String baseId = String.valueOf(originId);
        String[] baseIdArray = baseId.split("");
        int baseIdLength = baseId.length();
        int otheroffset = ID_SIZE - baseIdLength;
        long offset = originId % otheroffset;
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < baseIdLength; i++) {
            if (i < otheroffset) {
                if (i % 2 == 0) {
                    sb.append(baseIdArray[i]).append(saltArrays[i + (int) offset]);
                } else {
                    sb.append(saltArrays[i + (int) offset]).append(baseIdArray[i]);
                }
            } else {
                sb.append(baseIdArray[i]);
            }
        }
        return sb.toString();
    }


    StampedSequence nextSequence() {
        //下一个时间戳序列
        StampedSequence nextSequence = new StampedSequence(0, 0);

        //版本
        int[] versionHolder = new int[1];
        while (true) {
            long now = System.currentTimeMillis();
            StampedSequence curSequence = current.get(versionHolder);
            if (now < curSequence.timestamp) {
                throw new RuntimeException("Clock moved backwards!");
            } else if (curSequence.timestamp == now) {
                if (curSequence.sequence >= MAX_SEQUENCE) {
                    //满序列等待下一毫秒
                    continue;
                }
                nextSequence.timestamp = curSequence.timestamp;
                nextSequence.sequence = curSequence.sequence + 2;
                boolean set = current.compareAndSet(curSequence, nextSequence, versionHolder[0], versionHolder[0] + 1);
                if (!set) {
                    // 无锁更新失败，重新获取
                    continue;
                }
                break;
            } else {
                nextSequence.timestamp = now;
                nextSequence.sequence = 0;
                boolean set = current.compareAndSet(curSequence, nextSequence, versionHolder[0], versionHolder[0] + 1);
                if (!set) {
                    // 无锁更新失败，重新获取
                    continue;
                }
                break;
            }
        }
        return nextSequence;
    }

    /**
     * 带时间戳的序列
     */
    static class StampedSequence {
        private long timestamp; // 时间戳
        private long sequence; // 序列

        public StampedSequence(long stamp, long sequence) {
            super();
            this.timestamp = stamp;
            this.sequence = sequence;
        }
    }
}
